-module(socket_protocol).
-behaviour(gen_server).
-behaviour(ranch_protocol).
-define(TIMEOUT, 30 * 1000).

-export([start_link/0, start_link/4]).
-export([
    init/1,
    handle_call/3,
    handle_cast/2,
    handle_info/2,
    terminate/2,
    code_change/3
]).

start_link() ->
    gen_server:start_link(?MODULE, [], []).

start_link(Ref, Socket, Transport, ProtoOpts) ->
    {ok, proc_lib:spawn_link(?MODULE, init, [{Ref, Socket, Transport, ProtoOpts}])}.
    % proc_lib:start_link(?MODULE, init, [{Ref, Socket, Transport, ProtoOpts}]).
    % init 同步调用ok = proc_lib:init_ack({ok, self()}),

init({Ref, Socket, Transport, ProtoOpts}) ->
    {ok, {Ip, Port}} = inet:peername(Socket),
    IpStr = unicode:characters_to_binary(inet:ntoa(Ip)),
    ok = ranch:accept_ack(Ref),
    ok = Transport:setopts(Socket, [binary, {packet, 2}, {active, once}, {keepalive, true}]),
    ScoketMap = #{socket => Socket, transport => Transport, remote_ip => IpStr, remote_port => Port},
    gen_server:enter_loop(?MODULE, [], maps:merge(ScoketMap, ProtoOpts), ?TIMEOUT).

handle_call(_Request, _From, State) ->
    {reply, ok, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info({tcp, Socket, BinData}, #{socket := Socket, transport := Transport} = State) when byte_size(BinData) > 1 ->
    try
        ok = Transport:setopts(Socket, [{active, once}]),
        ok = handle_msg(BinData, State),
        {noreply, State, ?TIMEOUT}
    catch
        _Error ->
            {stop, normal, State}
    end;
handle_info({tcp_closed, Socket}, #{socket := Socket} = State) ->
    {stop, normal, State};
handle_info({tcp_error, _Socket, Reason}, State) ->
    {stop, Reason, State};
handle_info(timeout, State) ->
    {stop, normal, State};
handle_info({send_msg, RsMsg}, State) ->
    try 
        ok = do_send_msg(State, RsMsg),
        {noreply, State, ?TIMEOUT}
    catch
        _Error -> 
            {stop, normal, State}
    end;
      
handle_info(_Info, State) ->
    {stop, normal, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

handle_msg(RqBinData, State) ->
    #{handler := Handler, decoder := Decoder, encoder := Encoder, socket := Socket, transport := Transport} = State,
    {ok, RqMsg} = Decoder:decode(RqBinData),
    {ok, RsMsg} = Handler:handler(State, RqMsg),
    {ok, RsBinData} = Encoder:encode(RsMsg),    
    Transport:send(Socket, RsBinData),
    ok.

do_send_msg(RsMsg, State) ->
    #{encoder := Encoder, socket := Socket, transport := Transport} = State,
    {ok, RsBinData} = Encoder:encode(RsMsg), 
    Transport:send(Socket, RsBinData),
    ok.